Red Hat Enterprise LinuxのHigh Availability Add-OnでActive/Standby構成のクラスターを組んでみた
クラスターはロマンの塊
こんにちは、のんピ です。
皆さんはクラスターに興奮したことはありますか? 私はあります。
クラスターを構成することで、障害発生の絶体絶命のピンチもギリギリ耐え凌ぐことができる。そんなロマンを感じずにはいられません。
ただクラスターを構成するためのソフトの設定は一癖も二癖もあったりします。
AWSではそんなクラスターソフトが既にインストール済みのAMIが、以下の通り提供されています。
お客様は、Amazon EC2 のスケール、パフォーマンス、および伸縮自在性を、Red Hat Enterprise Linux (RHEL) with High availability (HA) と組み合わせて、ミッションクリティカルなワークロード向けに、信頼性と可用性の高いコンピューティングクラスターを簡単に構築できます。RHEL with HA AMI は、ソフトウェアパッケージで事前設定されており、お客様がインスタンスの構築と保守に使用できる Red Hat の高可用性アドオンリポジトリにアクセスできます。RHEL with HA は、Amazon EC2 コンソールおよびマーケットプレイスから直接起動できます。
Red Hat Enterprise Linux with High availability for Amazon EC2 が利用可能に
今回は、このRed Hat Enterprise Linux(以下RHEL)のHigh Availability Add-Onが含まれているAMIを使って、Active/Standby構成のクラスターを組んでみようと思います。
いきなりまとめ
- High Availability Add-On(以下HA Add-On)は、Red Hatが提供している、クラスターを構成するのに必要なサービスがまとまったアドオン製品
- HA Add-On単体では、ノード間のボリューム同期はできない。ノード間でボリュームの同期が必要な場合は、Resilient Storage Add-Onなど、別の製品が必要
- HA Add-Oのインスタンス料金は、通常のRHELのインスタンス料金より高い
High Availability Add-On ってなに??
High Availability Add-On(HA Add-On)とは、Red Hatが提供している、クラスターを構成するのに必要なサービスがまとまったアドオン製品です。
具体的には以下のようなサービスが含まれています。
サービス | 役割 |
---|---|
pacemaker | クラスター内のリソース(IPアドレスや、アプリケーションなど)の起動 / 停止や、 障害検出、フェールオーバーなどを行うクラスターの核となるサービスです。 |
corosync | クラスター内のノード間通信を管理し、ノード間で死活監視を行うサービスです。 |
pcs (pcsd) | クラスターの設定管理ツールです。クラスターの作成 / 起動 / 停止を行うサービスです。 pcsがCLIで、pcsdはWeb UIの管理ツールになります。 |
fence agent | リソースやノード障害時に、クラスターから問題のあるノードを切り離す処理を実行するサービスです。 |
また、以下公式ドキュメントの通り、HA Add-Onだけでなく、他のAdd-Onを使うことで、様々な要件のクラスターを構成することができます。
以下のコンポーネントで、High Availability Add-On を補完できます。
- Red Hat GFS2 (Global File System 2) - Resilient Storage Add-On に同梱され、High Availability Add-On で使用するクラスターファイルシステムを提供します。GFS2 により、ストレージがローカルで各クラスターノードに接続されているかのように、ブロックレベルにおいて、複数ノードでストレージを共有できるようになります。GFS2 クラスターファイルシステムを使用する場合は、クラスターインフラストラクチャーが必要になります。
LVM ロッキングデーモン (lvmlockd): Resilient Storage Add-On に同梱され、クラスターストレージのボリューム管理を提供します。lvmlockd に対応するには、クラスターインフラストラクチャーも必要になります。
Load Balancer Add-On - レイヤー 4 (TCP) およびレイヤー 7 (HTTP および HTTPS) サービスで高可用性負荷分散とフェイルオーバーを提供するルーティングソフトウェアです。Load Balancer Add-On は、負荷アルゴリズムを使用して、クライアント要求を実サーバーに分散する冗長な仮想ルーターのクラスターで実行し、まとまって仮想サーバーとして機能します。Load Balancer Add-On は、Pacemaker と併用する必要はありません。
1. High Availability Add-On の概要 - 1.1. High Availability Add-On コンポーネント
追加の課金は発生する??
オンデマンド料金に含まれる形で、追加の課金が発生します。
EC2の料金表で、オペレーティングシステムのプルダウンメニューを開くと、RHEL
と、Red Hat Enterprise Linux HA
があることが確認できます。
試しに2つのOSのm5、c5のインスタンスファミリーのオンデマンド料金(東京リージョン)を見比べてみました。
- m5 の比較
インスタンスタイプ | RHELのオンデマンドインスタンスの 時間単価 |
Red Hat Enterprise Linux HAの オンデマンドインスタンスの時間単価 |
料金比較 |
---|---|---|---|
m5.large | 0.184 USD | 0.219 USD | 119.02% |
m5.xlarge | 0.308 USD | 0.343 USD | 111.36% |
m5.2xlarge | 0.626 USD | 0.661 USD | 105.59% |
m5.4xlarge | 1.122 USD | 1.157 USD | 103.12% |
m5.8xlarge | 2.114 USD | 2.149 USD | 101.66% |
m5.12xlarge | 3.106 USD | 3.141 USD | 101.13% |
m5.16xlarge | 4.098 USD | 4.133 USD | 100.85% |
m5.24xlarge | 6.082 USD | 6.117 USD | 100.58% |
m5.metal | 6.082 USD | 6.117 USD | 100.58% |
- c5 の比較
インスタンスタイプ | RHELのオンデマンドインスタンスの 時間単価 |
Red Hat Enterprise Linux HAの オンデマンドインスタンスの時間単価 |
料金比較 |
---|---|---|---|
c5.large | 0.167 USD | 0.202 USD | 120.96% |
c5.xlarge | 0.274 USD | 0.309 USD | 112.77% |
c5.2xlarge | 0.558 USD | 0.593 USD | 106.27% |
c5.4xlarge | 0.986 USD | 1.021 USD | 103.55% |
c5.9xlarge | 2.056 USD | 2.091 USD | 101.70% |
c5.12xlarge | 2.698 USD | 2.733 USD | 101.30% |
c5.18xlarge | 3.982 USD | 4.017 USD | 100.88% |
c5.24xlarge | 5.266 USD | 5.301 USD | 100.66% |
c5.metal | 5.266 USD | 5.301 USD | 100.66% |
どちらのインスタンスファミリーも、インスタンスサイズが大きくなればなるほど、Red Hat Enterprise Linux HAのオンデマンド料金はRHELのオンデマンド料金に近似していくことが分かります。
クラスター構成を組む必要がある場合、ユースケースにもよりますが、処理性能などの関係で大きめのインスタンスサイズを選択することが多いと思います。大きめのインスタンスサイズを選択したとしても、素のRHELからそこまで大きな追加課金が派生しないのは嬉しいポイントですね。
やってみた
検証環境
今回検証を行う環境は以下の通りです。
3つEC2インスタンスを用意したのはスプリッドブレイン対策です。 HA Add-Onでは、クォーラムデバイスによるスプリッドブレイン対策も可能ですが、今回は3ノード構成でクラスターを組んでみます。
クォーラムデバイスについてはこちらのRed Hatの公式ドキュメントをご確認ください。
AWS CDKでデプロイ
毎度のごとく、AWS CDKでデプロイします。ディレクトリの構成は以下の通りです。
> tree . ├── .gitignore ├── .npmignore ├── README.md ├── bin │ └── app.ts ├── cdk.context.json ├── cdk.json ├── jest.config.js ├── lib │ └── app-stack.ts ├── package-lock.json ├── package.json ├── src │ └── ec2 │ └── userDataRHEL8.sh ├── test │ └── app.test.ts └── tsconfig.json 5 directories, 13 files
メインで動かすのは./lib/app-stack.ts
です。ここで全てのリソースを作成しています。
RHEL with HAのAMIの名前はRHEL_HA-8.4.0_HVM-20210504-x86_64-2-Hourly2-GP2
のようなフォーマットになっています。そのためバージョンが変わっても良いように、正規表現でRHEL_HA-8.*_HVM-*
と表記しました。
また、Red Hatが提供しているAMIの所有者IDは309956199498
なので、併せて指定しています。
実際のコードは以下の通りです。
import * as cdk from "@aws-cdk/core"; import * as ec2 from "@aws-cdk/aws-ec2"; import * as iam from "@aws-cdk/aws-iam"; import * as elbv2 from "@aws-cdk/aws-elasticloadbalancingv2"; import * as fs from "fs"; export class AppStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); // Create SSM IAM role const ssmIamRole = new iam.Role(this, "SsmIamRole", { assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName( "AmazonSSMManagedInstanceCore" ), ], }); // Create IAM policy for Lambda to operate EC2 instances. const ssmIamPolicy = new iam.Policy(this, "SsmIamPolicy", { statements: [ new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ["iam:PassRole"], resources: [ssmIamRole.roleArn], }), new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ["ec2:StartInstances", "ec2:StopInstances"], resources: [ `arn:aws:ec2:${new cdk.ScopedAws(this).region}:${ new cdk.ScopedAws(this).accountId }:instance/*`, ], }), new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ["ec2:DescribeInstances", "ec2:DescribeInstanceStatus"], resources: ["*"], }), ], }); // Attach an IAM policy to the IAM role for Lambda to operate EC2 instances. ssmIamRole.attachInlinePolicy(ssmIamPolicy); // Create VPC const vpc = new ec2.Vpc(this, "Vpc", { cidr: "10.0.0.0/16", enableDnsHostnames: true, enableDnsSupport: true, natGateways: 3, maxAzs: 3, subnetConfiguration: [ { name: "Public", subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 }, { name: "Private", subnetType: ec2.SubnetType.PRIVATE, cidrMask: 24 }, ], }); // Security Group for ALB const albSg = new ec2.SecurityGroup(this, "AlbSg", { allowAllOutbound: true, vpc: vpc, }); albSg.addIngressRule( ec2.Peer.anyIpv4(), ec2.Port.tcp(80), "Allow inbound HTTP" ); // Security Group for HA node const haSg = new ec2.SecurityGroup(this, "HaSg", { allowAllOutbound: true, vpc: vpc, }); haSg.addIngressRule( haSg, ec2.Port.allTraffic(), "Allow HA nodes to communicate with each other" ); haSg.addIngressRule(albSg, ec2.Port.tcp(80), "Allow web access from alb"); // Create ALB const alb = new elbv2.ApplicationLoadBalancer(this, "Alb", { vpc: vpc, vpcSubnets: vpc.selectSubnets({ subnetGroupName: "Public" }), internetFacing: true, securityGroup: albSg, }); // Create ALB Target group const targetGroup = new elbv2.ApplicationTargetGroup(this, "TargetGroup", { vpc: vpc, port: 80, protocol: elbv2.ApplicationProtocol.HTTP, targetType: elbv2.TargetType.INSTANCE, healthCheck: { path: "/phpinfo.php", healthyHttpCodes: "200", healthyThresholdCount: 2, unhealthyThresholdCount: 2, }, }); // Create ALB listener const listener = alb.addListener("Listener", { port: 80, defaultTargetGroups: [targetGroup], }); // User data for RHEL 8 const userDataParameter = fs.readFileSync( "./src/ec2/userDataRHEL8.sh", "utf8" ); const userDataRhel8 = ec2.UserData.forLinux({ shebang: "#!/bin/bash -xe", }); userDataRhel8.addCommands(userDataParameter); // Create EC2 instance // Red Hat Enterprise Linux 8 with High Availability vpc .selectSubnets({ subnetGroupName: "Private" }) .subnets.forEach((subnet, index) => { const ec2Instance = new ec2.Instance(this, `RHEL8HA${index}`, { machineImage: ec2.MachineImage.lookup({ name: "RHEL_HA-8.*_HVM-*", owners: ["309956199498"], }), instanceType: new ec2.InstanceType("t3.micro"), vpc: vpc, securityGroup: haSg, keyName: this.node.tryGetContext("key-pair"), role: ssmIamRole, vpcSubnets: vpc.selectSubnets({ subnetGroupName: "Private", availabilityZones: [vpc.availabilityZones[index]], }), userData: userDataRhel8, }); targetGroup.addTarget( new elbv2.InstanceTarget(ec2Instance.instanceId, 80) ); }); } }
User Dataは別ファイルに書いたものを読み込ませて、EC2インスタンス起動時に実行させています。
実行させている内容は以下の通りです。
- EC2 インスタンスのコンソールログにUser Dataの内容を出力を送信するように設定
- SSM Agentのインストール
- RHELではデフォルトでインストールされていないので、SSMを使用するのであれば必須です。
- 必要なパッケージのインストール
- AWS上でフェンシング(クラスターから問題のあるノードを切り離す処理)を行うため、
fence-agents-aws
が必須です。
- AWS上でフェンシング(クラスターから問題のあるノードを切り離す処理)を行うため、
- Apacheを動作させるために各種権限設定を変更
- phpinfo.phpの作成
- Apacheの拡張ステータス表示の有効化
- ApacheがIPv4でのみ LISTEN するように設定
pcsd.service
の起動、自動起動の有効化- AWS CLI v2のインストールと、シンボリックリンクの作成
fence-agents-aws
の裏で AWS CLIが動作するようなのでインストールします。- AWS CLIのインストール先はデフォルトだと、
/usr/local/bin/aws
ですが、fence-agents-aws
は/usr/bin/aws
しか見ないようなので、シンボリックリンクを作成します。
実際のコードは以下の通りです。
# Redirect /var/log/user-data.log and /dev/console exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1 # Install and start SSM Agent dnf install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm systemctl enable amazon-ssm-agent systemctl start amazon-ssm-agent # Install the necessary packages. dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm dnf install -y http://rpms.remirepo.net/enterprise/remi-release-8.rpm dnf module enable -y php:remi-8.0 dnf install -y httpd php wget unzip dnf install -y fence-agents-aws # Update packages. dnf update -y # Add ec2-user to the apache group. usermod -a -G apache ec2-user # Change the group ownership of /var/www and its contents to the apache group. chown -R ec2-user:apache /var/www # Change the directory permissions for /var/www and its subdirectories to set write permission for the group, and set the group ID for future subdirectories. chmod 2775 /var/www find /var/www -type d -exec chmod 2775 {} \; # Repeatedly change the file permissions for /var/www and its subdirectories to add group write permissions. find /var/www -type f -exec chmod 0664 {} \; # Add phpinfo.php echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php # Enable extended status display tee /etc/httpd/conf.d/status.conf <<"EOF" >/dev/null <Location /server-status> SetHandler server-status Order deny,allow Deny from all Allow from 127.0.0.1 Allow from ::1 </Location> EOF # Fixed to LISTEN only for IPv4. sed -i -e "s/^Listen 80/Listen 0.0.0.0:80/" /etc/httpd/conf/httpd.conf # Start up pcsd.service, the configuration tool for Pacemaker and Corosync. systemctl start pcsd.service systemctl enable pcsd.service # Create a user for the cluster echo Y-PfN8zQb@7xc46@3yX4 | passwd --stdin hacluster # Install AWS CLI v2 and create a symbolic link to /usr/bin/aws curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip ./aws/install ln -s /usr/local/bin/aws /usr/bin/aws
cdk deploy
後に確認をしてみると、EC2インスタンスが3台起動していることが確認できます。
High Availability Add-On の設定
クラスターの作成
それでは、OSにログインしてクラスターの設定をしていきます。
設定をするにあたって、こちらのRed Hatの公式ドキュメントを参考しながら行いました。
まず、クラスターの操作をするためのサービスであるpcsd.service
が起動しているか確認します。
確認をしてみると以下の通り、サービスが起動していますね。
sh-4.4$ systemctl status pcsd.service ● pcsd.service - PCS GUI and remote configuration interface Loaded: loaded (/usr/lib/systemd/system/pcsd.service; enabled; vendor preset: disabled) Active: active (running) since Tue 2021-06-08 07:02:04 UTC; 1min 13s ago Docs: man:pcsd(8) man:pcs(8) Main PID: 38195 (pcsd) Tasks: 1 (limit: 4625) Memory: 24.0M CGroup: /system.slice/pcsd.service └─38195 /usr/libexec/platform-python -Es /usr/sbin/pcsd[
次に、クラスターを構成するノードに対して、User Dataで作成したクラスター用ユーザー(hacluster
)で認証をします。
認証をする際は、以下のようなフォーマットのコマンドを実行します。
pcs host auth hostname1 hostname2 hostname3
このhostname
は、各ノードのプライベートIPv4 DNSを指定しました。
実際の操作ログは以下の通りです。全てのノードでAuthorized
と表示されているので、正しく認証されていますね。
sh-4.4$ sudo pcs host auth ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal Username: hacluster Password: ip-10-0-3-58.ec2.internal: Authorized ip-10-0-5-8.ec2.internal: Authorized ip-10-0-4-38.ec2.internal: Authorized sh-4.4$
次に、クラスターを作成します。
クラスターを作成する際は、以下のようなフォーマットのコマンドを実行します。
pcs cluster setup cluster-name hostname1 hostname2 hostname3
実際の操作ログは以下の通りです。corosync
とpacemaker
の認証情報が各ノードに渡され、corosync
の設定ファイルも各ノードに配布されていることが確認できます。
sh-4.4$ sudo pcs cluster setup web-cluster --start ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal No addresses specified for host 'ip-10-0-3-58.ec2.internal', using 'ip-10-0-3-58.ec2.internal' No addresses specified for host 'ip-10-0-4-38.ec2.internal', using 'ip-10-0-4-38.ec2.internal' No addresses specified for host 'ip-10-0-5-8.ec2.internal', using 'ip-10-0-5-8.ec2.internal' Destroying cluster on hosts: 'ip-10-0-3-58.ec2.internal', 'ip-10-0-4-38.ec2.internal', 'ip-10-0-5-8.ec2.internal'... ip-10-0-3-58.ec2.internal: Successfully destroyed cluster ip-10-0-5-8.ec2.internal: Successfully destroyed cluster ip-10-0-4-38.ec2.internal: Successfully destroyed cluster Requesting remove 'pcsd settings' from 'ip-10-0-3-58.ec2.internal', 'ip-10-0-4-38.ec2.internal', 'ip-10-0-5-8.ec2.internal' ip-10-0-3-58.ec2.internal: successful removal of the file 'pcsd settings' ip-10-0-4-38.ec2.internal: successful removal of the file 'pcsd settings' ip-10-0-5-8.ec2.internal: successful removal of the file 'pcsd settings' Sending 'corosync authkey', 'pacemaker authkey' to 'ip-10-0-3-58.ec2.internal', 'ip-10-0-4-38.ec2.internal', 'ip-10-0-5-8.ec2.internal' ip-10-0-3-58.ec2.internal: successful distribution of the file 'corosync authkey' ip-10-0-3-58.ec2.internal: successful distribution of the file 'pacemaker authkey' ip-10-0-4-38.ec2.internal: successful distribution of the file 'corosync authkey' ip-10-0-4-38.ec2.internal: successful distribution of the file 'pacemaker authkey' ip-10-0-5-8.ec2.internal: successful distribution of the file 'corosync authkey' ip-10-0-5-8.ec2.internal: successful distribution of the file 'pacemaker authkey' Sending 'corosync.conf' to 'ip-10-0-3-58.ec2.internal', 'ip-10-0-4-38.ec2.internal', 'ip-10-0-5-8.ec2.internal' ip-10-0-3-58.ec2.internal: successful distribution of the file 'corosync.conf' ip-10-0-4-38.ec2.internal: successful distribution of the file 'corosync.conf' ip-10-0-5-8.ec2.internal: successful distribution of the file 'corosync.conf' Cluster has been successfully set up. Starting cluster on hosts: 'ip-10-0-3-58.ec2.internal', 'ip-10-0-4-38.ec2.internal', 'ip-10-0-5-8.ec2.internal'... sh-4.4$
クラスターの有効化と起動
続いて、クラスターの有効化と起動を行います。
クラスターの有効化と起動を行う際は、以下のようなフォーマットのコマンドを実行します。
# クラスターの有効化 # クラスターを構成している全てのノードに対して、OS起動時にクラスターサービスが実行されるよう設定 pcs cluster enable --all # クラスターの起動 # クラスターを構成している全てのノードに対して、クラスターサービスを起動する pcs cluster start --all
実際の操作ログは以下の通りです。全てのノードに対して正常に操作が完了していることが確認できます。
sh-4.4$ sudo pcs cluster enable --all ip-10-0-3-58.ec2.internal: Cluster Enabled ip-10-0-4-38.ec2.internal: Cluster Enabled ip-10-0-5-8.ec2.internal: Cluster Enabled sh-4.4$ sh-4.4$ sh-4.4$ sudo pcs cluster start --all ip-10-0-3-58.ec2.internal: Starting Cluster... ip-10-0-5-8.ec2.internal: Starting Cluster... ip-10-0-4-38.ec2.internal: Starting Cluster...
フェンシングの設定
次にフェンシングの設定をします。
改めてフェンシングとは何かを確認します。 公式ドキュメントには以下のような記載があります。
クラスター内のノードの 1 つと通信が失敗した場合に、障害が発生したクラスターノードがアクセスする可能性があるリソースへのアクセスを、その他のノードが制限したり、解放したりできるようにする必要があります。クラスターノードが応答しない可能性があるため、そのクラスターノードと通信しても成功しません。代わりに、フェンスエージェントを使用した、フェンシングと呼ばれる外部メソッドを指定する必要があります。フェンスデバイスは、クラスターが使用する外部デバイスのことで、このデバイスを使用して、不安定なノードによる共有リソースへのアクセスを制限したり、クラスタノードでハードリブートを実行します。
フェンスデバイスが設定されていないと、以前使用していたリソースが解放されていることを切断されているクラスターノードが把握できず、他のクラスターノードでサービスを実行できなくなる可能性があります。また、クラスターノードがそのリソースを解放したとシステムが誤って想定し、データが破損または損失する可能性もあります。フェンスデバイスが設定されていないと、データの整合性は保証できず、クラスター設定はサポートされません。
要するに、フェンシングとは障害発生時に複数のノードが同時にリソースにアクセスしないように排他処理をする仕組みです。
fence-agents-aws
の場合は、障害が発生したと判断したノードを再起動させます。
フェンシングの設定は、以下のようなフォーマットのコマンドを実行します。フェンシングの細かいプロパティについてはこちらのRed Hatの公式ドキュメントをご覧ください。
pcs stonith create name fence_aws access_key=access-key secret_key=secret-access-key region=region pcmk_host_map="rhel-hostname-1:Instance-ID-1;rhel-hostname-2:Instance-ID-2;rhel-hostname-3:Instance-ID-3" power_timeout=240 pcmk_reboot_timeout=480 pcmk_reboot_retries=4
実際の操作ログは以下の通りです。なお、今回はIAMロールで認証情報を渡しているので、access_key
と、secret_key
は指定していません。
sh-4.4$ sudo pcs stonith create clusterfence fence_aws region=us-east-1 pcmk_host_map="ip-10-0-3-58.ec2.internal:i-075fbb3489e5e7368;ip-10-0-4-38.ec2.internal:i-003c77cde1b375e02;ip-10-0-5-8.ec2.internal:i-0f279cee7b3c60a79" power_timeout=240 pcmk_reboot_timeout=480 pcmk_reboot_retries=4
フェンシング設定後、クラスターのステータスを確認します。クラスターのステータスを確認すると、clusterfence
というフェンシング用のリソースが作成できていることが確認できます。
sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-5-8.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 07:08:48 2021 * Last change: Tue Jun 8 07:07:54 2021 by root via cibadmin on ip-10-0-3-58.ec2.internal * 3 nodes configured * 1 resource instance configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
それではフェンシングのテストをしてみます。手動でフェンシングを実行した後、クラスターのステータスを確認してみます。
実際の操作ログは以下の通りです。フェンシングをしたip-10-0-5-8.ec2.internal
のステータスが、OFFLINE
になっていることが確認できます。
sh-4.4$ sudo pcs stonith fence ip-10-0-5-8.ec2.internal Node: ip-10-0-5-8.ec2.internal fenced sh-4.4$ sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 07:13:38 2021 * Last change: Tue Jun 8 07:07:54 2021 by root via cibadmin on ip-10-0-3-58.ec2.internal * 3 nodes configured * 1 resource instance configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ] * OFFLINE: [ ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
マネージメントコンソールを確認すると、ip-10-0-5-8.ec2.internal
が停止->起動していることが確認できます。
ip-10-0-5-8.ec2.internal
が起動完了するまで待った後、クラスターのステータスを確認すると、ip-10-0-5-8.ec2.internal
のステータスが、Online
になっていることが確認できます。
sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 07:14:50 2021 * Last change: Tue Jun 8 07:07:54 2021 by root via cibadmin on ip-10-0-3-58.ec2.internal * 3 nodes configured * 1 resource instance configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
サービスの登録
それでは、最後にクラスターにサービスを登録します。
まず、クラスターに登録するサービスにどのようなパラメーターがあるのか確認をします。サービスのパラメーターの確認は、以下のようなフォーマットのコマンドを実行します。
pcs resource describe resourcetype
今回クラスターに登録するサービスはApacheとPHP-FPMです。なお、以下の通り、RHEL8系よりPHPはモジュール版ではなく、FastCGI版がデフォルトになっています。
Red Hat Enterprise Linux 8 には PHP 7.2 が同梱されています。このバージョンには、RHEL 7 で利用できた PHP 5.4 に対する重要な変更が追加されています。
PHP はデフォルトで FastCGI Process Manager (FPM) を使用します (スレッド化された httpd で安全に使用できます)。
実際の操作ログは以下の通りです。Apacheはconfigfile
や、client
など複数の指定可能なパラメーターがありますが、PHP-FPMは設定できるパラメーターはないようですね。
sh-4.4$ pcs resource describe apache Assumed agent name 'ocf:heartbeat:apache' (deduced from 'apache') ocf:heartbeat:apache - Manages an Apache Web server instance This is the resource agent for the Apache Web server. This resource agent operates both version 1.x and version 2.x Apache servers. The start operation ends with a loop in which monitor is repeatedly called to make sure that the server started and that it is operational. Hence, if the monitor operation does not succeed within the start operation timeout, the apache resource will end with an error status. The monitor operation by default loads the server status page which depends on the mod_status module and the corresponding configuration file (usually /etc/apache2/mod_status.conf). Make sure that the server status page works and that the access is allowed *only* from localhost (address 127.0.0.1). See the statusurl and testregex attributes for more details. See also http://httpd.apache.org/ Resource options: configfile (unique): The full pathname of the Apache configuration file. This file is parsed to provide defaults for various other resource agent parameters. httpd: The full pathname of the httpd binary (optional). port: A port number that we can probe for status information using the statusurl. This will default to the port number found in the configuration file, or 80, if none can be found in the configuration file. statusurl: The URL to monitor (the apache server status page by default). If left unspecified, it will be inferred from the apache configuration file. If you set this, make sure that it succeeds *only* from the localhost (127.0.0.1). Otherwise, it may happen that the cluster complains about the resource being active on multiple nodes. testregex: Regular expression to match in the output of statusurl. Case insensitive. client: Client to use to query to Apache. If not specified, the RA will try to find one on the system. Currently, wget and curl are supported. For example, you can set this parameter to "curl" if you prefer that to wget. testurl: URL to test. If it does not start with "http", then it's considered to be relative to the Listen address. testregex10: Regular expression to match in the output of testurl. Case insensitive. testconffile: A file which contains test configuration. Could be useful if you have to check more than one web application or in case sensitive info should be passed as arguments (passwords). Furthermore, using a config file is the only way to specify certain parameters. Please see README.webapps for examples and file description. testname: Name of the test within the test configuration file. options: Extra options to apply when starting apache. See man httpd(8). envfiles: Files (one or more) which contain extra environment variables. If you want to prevent script from reading the default file, set this parameter to empty string. use_ipv6: We will try to detect if the URL (for monitor) is IPv6, but if that doesn't work set this to true to enforce IPv6. Default operations: start: interval=0s timeout=40s stop: interval=0s timeout=60s monitor: interval=10s timeout=20s sh-4.4$ sh-4.4$ sh-4.4$ pcs resource describe service:php-fpm service:php-fpm - systemd unit file for php-fpm The PHP FastCGI Process Manager Default operations: start: interval=0s timeout=100 stop: interval=0s timeout=100 monitor: interval=60 timeout=100 sh-4.4$
それでは、クラスターにApacheとPHP-FPMを登録します。
クラスターへのサービスの登録は、以下のようなフォーマットのコマンドを実行します。
pcs resource create resource_id [standard:[provider:]]type [resource_options] [op operation_action operation_options [operation_action operation options]...] [meta meta_options...] [clone [clone_options] | master [master_options] | --group group_name [--before resource_id | --after resource_id] | [bundle bundle_id] [--disabled] [--wait[=n]]
実際の操作ログは以下の通りです。ApacheとPHP-FPMを同じノードで動作させたいので、webgroup
というグループに含まれるように設定します。
サービス登録後にクラスターのステータスを確認すると、webgroup
にApache
とPHP-FPM
が登録され起動していることが確認できます。
sh-4.4$ sudo pcs resource create Apache ocf:heartbeat:apache configfile=/etc/httpd/conf/httpd.conf statusurl="http://localhost/server-status" client=wget --group webgroup sh-4.4$ sh-4.4$ sudo pcs resource create PHP-FPM service:php-fpm --group webgroup sh-4.4$ sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 07:23:04 2021 * Last change: Tue Jun 8 07:22:56 2021 by root via cibadmin on ip-10-0-3-58.ec2.internal * 3 nodes configured * 3 resource instances configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal * Resource Group: webgroup: * Apache (ocf::heartbeat:apache): Started ip-10-0-4-38.ec2.internal * PHP-FPM (service:php-fpm): Started ip-10-0-4-38.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
マネージメントコンソールでターゲットグループを確認すると、webgroup
のリソースが起動しているip-10-0-4-38.ec2.internal (RHEL8HA1)
のステータスがhealthy
になっていました。
ターゲットグループのステータスがhealthy
になっていることから、サービスが正しく起動していそうですね。
それでは、実際にブラウザでアクセスしてphpinfoが表示できるか確認します。
以下の通り、phpinfoが表示されています。System
に記載されているホスト名も、クラスターのステータスで確認した通り、ip-10-0-4-38.ec2.internal
となっていることが確認できます。
High Availability Add-On のフェールオーバー
手動でフェールオーバー
クラスターといえば、フェールオーバーです。実際にサービスがフェールオーバーするかを確認してみます。
まずは、手動でサービスをフェールオーバーしてみます。
手動でフェールオーバーするために、現在、webgroup
が稼働しているノードをスタンバイモードにします。
ノードがスタンバイモードになると、対象のノードはノード上のリソースを別のノードにフェールオーバーします。
ノードをスタンバイモードにする場合は、以下のようなフォーマットのコマンドを実行します。
pcs node standby node | --all
実際の操作ログは以下の通りです。webgroup
が稼働しているノード(ip-10-0-4-38.ec2.internal
)をスタンバイモードにして、クラスターのステータスを確認してみました。
Node List
でip-10-0-4-38.ec2.internal
がstandby
になり、リソースが稼働しているノードがip-10-0-4-38.ec2.internal
からip-10-0-5-8.ec2.internal
に変わっていることが確認できますね。
sh-4.4$ sudo pcs node standby ip-10-0-4-38.ec2.internal sh-4.4$ sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 07:26:05 2021 * Last change: Tue Jun 8 07:25:57 2021 by root via cibadmin on ip-10-0-3-58.ec2.internal * 3 nodes configured * 3 resource instances configured Node List: * Node ip-10-0-4-38.ec2.internal: standby * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal * Resource Group: webgroup: * Apache (ocf::heartbeat:apache): Started ip-10-0-5-8.ec2.internal * PHP-FPM (service:php-fpm): Starting ip-10-0-5-8.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
マネージメントコンソールからターゲットグループを確認すると、ステータスがhealthy
のインスタンスがRHEL8HA1
からRHEL8HA2
に変わっています。
また、phpinfo.php
にアクセスすると、System
に記載されているホスト名が、クラスターのステータスで確認した通り、フェールオーバー先のip-10-0-5-8.ec2.internal
となっていることが確認できます。
それでは、スタンバイモードにしていたip-10-0-4-38.ec2.internal
をスタンバイモードから解除します。
ノードをスタンバイモードから解除する場合は、以下のようなフォーマットのコマンドを実行します。
pcs node unstandby node | --all
実際の操作ログは以下の通りです。
Node List
でstandby
だった、ip-10-0-4-38.ec2.internal
がOnline
になっていることが確認できます。
sh-4.4$ sudo pcs node unstandby ip-10-0-4-38.ec2.internal sh-4.4$ sh-4.4$ sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 07:26:50 2021 * Last change: Tue Jun 8 07:26:46 2021 by root via cibadmin on ip-10-0-3-58.ec2.internal * 3 nodes configured * 3 resource instances configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal * Resource Group: webgroup: * Apache (ocf::heartbeat:apache): Started ip-10-0-5-8.ec2.internal * PHP-FPM (service:php-fpm): Started ip-10-0-5-8.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
カーネルパニックによるフェールオーバー
実際のフェールオーバーは障害発生によるものです。
そこで、以下の記事で紹介したように、診断割り込みAPIを実行してカーネルパニックを発生させて、フェールオーバーするか確認してみます。
まずは、以下コマンドで現在サービスが稼働しているip-10-0-5-8.ec2.internal
をカーネルパニックさせます。
> aws ec2 send-diagnostic-interrupt --instance-id i-0f279cee7b3c60a79 >
カーネルパニック発生後、クラスターのステータスを確認します。
実際の操作ログは以下の通りです。一時的にwebgroup
がStopped
になり、ip-10-0-4-38.ec2.internal
でサービスを再開していることが確認できます。
sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 08:16:38 2021 * Last change: Tue Jun 8 07:45:05 2021 by hacluster via crmd on ip-10-0-5-8.ec2.internal * 3 nodes configured * 3 resource instances configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal * Resource Group: webgroup: * Apache (ocf::heartbeat:apache): Stopped * PHP-FPM (service:php-fpm): Stopped Pending Fencing Actions: * reboot of ip-10-0-5-8.ec2.internal pending: client=pacemaker-controld.38962, origin=ip-10-0-4-38.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$ sh-4.4$ sh-4.4$ sudo pcs status Cluster name: web-cluster Cluster Summary: * Stack: corosync * Current DC: ip-10-0-4-38.ec2.internal (version 2.0.5-9.el8_4.1-ba59be7122) - partition with quorum * Last updated: Tue Jun 8 08:18:22 2021 * Last change: Tue Jun 8 07:45:05 2021 by hacluster via crmd on ip-10-0-5-8.ec2.internal * 3 nodes configured * 3 resource instances configured Node List: * Online: [ ip-10-0-3-58.ec2.internal ip-10-0-4-38.ec2.internal ip-10-0-5-8.ec2.internal ] Full List of Resources: * clusterfence (stonith:fence_aws): Started ip-10-0-3-58.ec2.internal * Resource Group: webgroup: * Apache (ocf::heartbeat:apache): Started ip-10-0-4-38.ec2.internal * PHP-FPM (service:php-fpm): Started ip-10-0-4-38.ec2.internal Daemon Status: corosync: active/enabled pacemaker: active/enabled pcsd: active/enabled sh-4.4$
phpinfo.php
にアクセスすると、System
に記載されているホスト名が、クラスターのステータスで確認した通り、フェールオーバー先のip-10-0-4-38.ec2.internal
となっていることが確認できます。
High Availability Add-Onを使えば、Red Hat Enterprise Linuxで簡単にActive/Standby構成のクラスターが組めますよ。
HA Add-Onで簡単にActive/Standby構成のクラスターが組めることが確認できました。
AWSでは可用性を向上させるために、ロードバランサーやAuto Scalingを使用して、Multi-AZでActive/Active構成にすることが多いと思います。
しかし、基幹系のシステムはActive/Active構成のクラスターを組むことが、ライセンスやソフトウェアの処理の都合などで難しい場合がよくあります。そのような場合に、今回紹介したHA Add-Onを使用することで、簡単に可用性を向上させることができると考えます。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!